#include "qmidihelper.h"
#include "allegro.h"

namespace midiHelper {

	const static char *g_NoteName[132] = {
		"C0", "C#0", "D0", "Eb0", "E0", "F0", "F#0", "G0", "G#0", "A0", "Bb0", "B0",
		"C1", "C#1", "D1", "Eb1", "E1", "F1", "F#1", "G1", "G#1", "A1", "Bb1", "B1",
		"C2", "C#2", "D2", "Eb2", "E2", "F2", "F#2", "G2", "G#2", "A2", "Bb2", "B2",
		"C3", "C#3", "D3", "Eb3", "E3", "F3", "F#3", "G3", "G#3", "A3", "Bb3", "B3",
		"C4", "C#4", "D4", "Eb4", "E4", "F4", "F#4", "G4", "G#4", "A4", "Bb4", "B4",
		"C5", "C#5", "D5", "Eb5", "E5", "F5", "F#5", "G5", "G#5", "A5", "Bb5", "B5",
		"C6", "C#6", "D6", "Eb6", "E6", "F6", "F#6", "G6", "G#6", "A6", "Bb6", "B6",
		"C7", "C#7", "D7", "Eb7", "E7", "F7", "F#7", "G7", "G#7", "A7", "Bb7", "B7",
		"C8", "C#8", "D8", "Eb8", "E8", "F8", "F#8", "G8", "G#8", "A8", "Bb8", "B8",
		"C9", "C#9", "D9", "Eb9", "E9", "F9", "F#9", "G9", "G#9", "A9", "Bb9", "B9",
		"C10", "C#10", "D10", "Eb10", "E10", "F10", "F#10", "G10", "G#10", "A10", "Bb10", "B10",
	};

	bool compare(const Alg_event_ptr s1, const Alg_event_ptr s2)
	{
		if (s1->time != s2->time)
		{
			return s1->time < s2->time;
		}

		return s1->chan < s2->chan;
	}

	QList<Alg_event_ptr> getMarkEvents(Alg_seq_ptr seq)
	{
		QList<Alg_event_ptr> marks;
		int nTracks = seq->tracks();
		for (int i = 0; i < seq->tracks(); i++)
		{
			Alg_track_ptr track_ptr = seq->track(i);
			int nTrackLength = track_ptr->length();
			for (int index = 0; index < track_ptr->length(); index++)
			{
				// for every event
				Alg_event_ptr evt = (*track_ptr)[index];

				// if the event is a update
				if (evt->get_type() == 'u')
				{
					char updateType = evt->get_update_type();
					if (updateType == 's')
					{
						const char* strValue = evt->get_attribute();
						if (strcmp(strValue, "markers") == 0 ||
							strcmp(strValue, "cues") == 0
							)
						{
							marks.push_back(evt);
						}
					}
				}
			}
		}

		qStableSort(marks.begin(), marks.end(), compare);
		return marks;
	}

	QList<QNote> getNoteEvents(Alg_seq_ptr seq)
	{
		seq->convert_to_beats();

		QList<QNote> notes;
		for (int j = 0; j < seq->track_list.length(); j++)
		{
			Alg_events &events = seq->track_list[j];

			// now write the notes at beat positions
			for (int i = 0; i < events.length(); i++)
			{
				Alg_event_ptr e = events[i];
				if (!e->is_note())
					continue;

				QNote note;
				note.track = j;
				note.channel = e->chan + 1;
				
				note.level = e->get_loud();
				note.pitch = e->get_pitch();

				note.ticks = static_cast<int>(e->time * seq->division + 0.5);
				note.duration = e->get_duration() * seq->division;

				note.data = QString(g_NoteName[e->get_identifier()]);
				note.MBT = ticksToMBT(seq, note.ticks);
				
				if (note.duration < seq->division)
				{
					note.Dur = QString("%1").arg(note.duration);
				}
				else
				{
					int beats = static_cast<int>(e->get_duration());
					int ticks = static_cast<int>(note.duration - beats * seq->division + 0.5);
					note.Dur = QString("%1:%2").arg(beats).arg(ticks, 3, 10, QLatin1Char('0'));
				}
				
				note.event = e;
				notes.push_back(note);
			}
		}

		seq->convert_to_seconds();
		for (auto& note : notes)
		{
			note.time = note.event->time;
			note.HMSF = secondsToHMSF(note.time);
			note.event = nullptr;
		}

		qStableSort(notes.begin(), notes.end(), &QNote::compare);
		return notes;
	}

	QString ticksToMBT(Alg_seq_ptr new_seq, double ticks)
	{
		QString MBT("");

		long lTicks = ticks;
		long ticksPerQuarterNote = new_seq->division;
		long bar = 1;
		long beat = 1;
		long tick = 0;
		long ticksUsed = 0;
		qint8 nTseNumerator = 4;
		qint8 nTseDenominator = 4;
		long lFrontTicks = 0;

		for (int tsx = 0; tsx < new_seq->time_sig.length(); tsx++)
		{
			auto& time_sig = new_seq->time_sig[tsx];
			long lTseTicks = time_sig.beat * ticksPerQuarterNote;
			if (lTicks + ticksUsed < lTseTicks)
				break;

			if (lTseTicks > 0 && lTicks + ticksUsed >= lTseTicks)
			{
				int beatsPerBar = (nTseNumerator != 0) ? nTseNumerator : 4;
				int ticksPerBar = (nTseNumerator * ticksPerQuarterNote * 4) / nTseDenominator;
				int ticksPerBeat = ticksPerBar / beatsPerBar;

				long lDeltaTicks = lTseTicks - lFrontTicks;
				bar += lDeltaTicks / ticksPerBar;
				beat += ((lDeltaTicks % ticksPerBar) / ticksPerBeat);
				tick += lDeltaTicks % ticksPerBeat;

				ticksUsed += lDeltaTicks;
				lTicks -= lDeltaTicks;
			}

			nTseNumerator = time_sig.num;
			nTseDenominator = time_sig.den;
			lFrontTicks = lTseTicks;
		}

		if (lTicks > 0)
		{
			int beatsPerBar = (nTseNumerator != 0) ? nTseNumerator : 4;
			int ticksPerBar = (nTseNumerator * ticksPerQuarterNote * 4) / nTseDenominator;
			int ticksPerBeat = ticksPerBar / beatsPerBar;
			bar += lTicks / ticksPerBar;
			beat += ((lTicks % ticksPerBar) / ticksPerBeat);
			tick += lTicks % ticksPerBeat;
		}

		MBT = QString("%1:%2:%3")
			.arg(bar, 2, 10, QLatin1Char('0'))
			.arg(beat, 2, 10, QLatin1Char('0'))
			.arg(tick, 3, 10, QLatin1Char('0'));
		return MBT;
	}

	QString secondsToHMSF(double timeInSeconds)
	{
		qint64 totalFrame = static_cast<qint64>(timeInSeconds * 30 + 0.5);
		int hour = static_cast<int>(totalFrame / 30 / 60 / 60);
		int minute = static_cast<int>(totalFrame / 30 / 60 - hour * 60);
		int second = static_cast<int>(totalFrame / 30 - minute * 60);
		int frame = static_cast<int>(totalFrame - second * 30);

		QString HMSF = QString("%1:%2:%3:%4")
			.arg(hour, 2, 10, QLatin1Char('0'))
			.arg(minute, 2, 10, QLatin1Char('0'))
			.arg(second, 2, 10, QLatin1Char('0'))
			.arg(frame, 2, 10, QLatin1Char('0'));

		return HMSF;
	}

} // midiHelper